package com.example.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.example.service.IRedisService;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Description redis 操作实现类
 * @Author Bing
 * @Date 2024/10/10 14:03
 */
@Service
public class RedisService implements IRedisService {

    @Resource
    private RedisTemplate redisTemplate;

    @Resource(name = "default")
    private RedisScript<Long> limitScript;

    @Resource(name = "limitWithValue")
    private RedisScript<Long> limitWithValueScript;

    @Override
    public Boolean getLock(String lockName, Object value, int effectiveTime) {
        return redisTemplate.opsForValue().setIfAbsent(lockName, value, effectiveTime, TimeUnit.SECONDS);
    }

    @Override
    public Boolean getLock(String lockName, int effectiveTime) {
        return redisTemplate.opsForValue().setIfAbsent(lockName, DateUtil.now(), effectiveTime, TimeUnit.SECONDS);
    }

    @Override
    public <T> void setValue(String key, T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    @Override
    public <T> void setValue(String key, T value, int effectiveTime) {
        redisTemplate.opsForValue().set(key, value, effectiveTime, TimeUnit.SECONDS);
    }

    @Override
    public Object getValue(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public <T> T getValue(String key, Class<T> clazz) {
        Object value = getValue(key);
        if(ObjectUtil.isNull(value)){
            return null;
        }
        return (T)value;
    }

    @Override
    public <T> void leftPush(String key, T value) {
        redisTemplate.opsForList().leftPush(key, value);
    }

    @Override
    public <T> void rightPush(String key, T value) {
        redisTemplate.opsForList().rightPush(key, value);
    }

    @Override
    public Object rightPop(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }

    @Override
    public <T> T rightPop(String key, Class<T> clazz) {
        Object value = rightPop(key);
        if(ObjectUtil.isNull(value)){
            return null;
        }
        return (T)value;
    }

    @Override
    public Object leftPop(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }

    @Override
    public <T> T leftPop(String key, Class<T> clazz) {
        Object value = leftPop(key);
        if(ObjectUtil.isNull(value)){
            return null;
        }
        return (T)value;
    }

    @Override
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public void delKey(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public void delKey(List<String> keys) {
        redisTemplate.delete(keys);
    }

    @Override
    public void expire(String key, int effectiveTime) {
        redisTemplate.expire(key, effectiveTime, TimeUnit.SECONDS);
    }

    @Override
    public <T> List<T> batch(List<String> keys, Class<T> clazz) {
        List<T> values = redisTemplate.opsForValue().multiGet(keys);
        return values;
    }

    @Override
    public boolean limitCount(String key,Integer count,Integer maxExpireTime) {
        Long result = (long)redisTemplate.execute(limitScript,
                Collections.singletonList(key),
                count,
                maxExpireTime);
        return result > count;
    }

    @Override
    public boolean limitWithStr(String key,Integer count,Integer maxExpireTime,String key2,String value) {
        Long result = (long)redisTemplate.execute(limitWithValueScript,
                CollUtil.toList(key,key2),
                count,
                maxExpireTime,
                value);
        return result > count;
    }

    @Override
    public void increment(String key, long step) {
        redisTemplate.opsForValue().increment(key, step);
    }

    @Override
    public Long getKeyExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    @Override
    public List<String> scanKeys(String pattern, int count) {
        Cursor<byte[]> scanCursor = redisTemplate.getConnectionFactory().getConnection().scan(ScanOptions.scanOptions().match(pattern).count(count).build());
        if(ObjectUtil.isNull(scanCursor)){
            return new ArrayList<>();
        }
        List<String> resultList = new ArrayList<>();
        while (scanCursor.hasNext()) {
            byte[] keyBytes = scanCursor.next();
            String key = new String(keyBytes);
            resultList.add(key);
        }
        return resultList;
    }
}